package com.ymatou.iapi.optrade.parameter.utill;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.converters.SqlTimestampConverter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Hours;
import org.joda.time.Minutes;
import org.joda.time.Seconds;

import com.ymatou.common.lang.CipherUtil;
import com.ymt.core.tool.Logger;
/**
 */
public class Utils {
	/**
	 * 默认格式
	 */
	public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

	public static String uuid() {
		return new ObjectId().toHexString();
	}

	/**
	 * 处理null问题
	 *
	 * @return
	 */
	public static BigDecimal zeroIfNull(BigDecimal number) {
		BigDecimal zero = BigDecimal.ZERO;
		if (number == null)
			number = zero;

		return number;
	}
	
	public static boolean nullIsZero(Long number) {
		if (number == null)
			number = 0L;
		
		if (number == 0)
			return false;
		else
			return true;
	}
	
	public static String emptyIfNull(String str) {
		
		if (null == str)
			return "";
		else
			return str;
	}

	/**
	* 是否大于
	* @param source 原值
	* @param compareTo 被比较值
	* @return
	*/
	public static boolean isBiggerThan(BigDecimal source, BigDecimal compareTo) {

		if (source == null) {
			source = BigDecimal.ZERO;
		}
		if (compareTo == null) {
			compareTo = BigDecimal.ZERO;
		}
		if (source.compareTo(compareTo) > 0) {
			return true;
		}
		return false;
	}
	/**
	* 是否大于或等
	* @param source 原值
	* @param compareTo 被比较值
	* @return
	*/
	public static boolean isBiggerThanOrEqual(BigDecimal source,
			BigDecimal compareTo) {
		if (source == null) {
			source = BigDecimal.ZERO;
		}
		if (compareTo == null) {
			compareTo = BigDecimal.ZERO;
		}
		if (source.compareTo(compareTo) >= 0) {
			return true;
		}
		return false;
	}

	/**
	 * 处理null问题
	 *
	 * @return
	 */
	public static Double zeroIfNull(Double number) {
		Double zero = 0D;
		if (number == null)
			number = zero;

		return number;
	}

	/**
	 * 处理null问题
	 *
	 * @return
	 */
	public static Integer zeroIfNull(Integer number) {
		Integer zero = 0;
		//return optional(number, zero);

		if (number == null)
			number = zero;

		return number;
	}
	/**
	 * 处理null问题
	 *
	 * @return
	 */
	public static Float zeroIfNull(Float number) {
		Float zero = 0F;
		if (number == null)
			number = zero;

		return number;
	}

	/**
	 * 处理null问题
	 *
	 * @return
	 */
	public static Long zeroIfNull(Long number) {
		Long zero = 0L;
		if (number == null)
			number = zero;

		return number;
	}

	/**
	 * 处理null问题
	 *
	 * @param b
	 * @return
	 */
	public static boolean falseIfNull(Boolean b) {

		if (b == null)
			b = false;

		return b;
	}
	
	public static String encryptAES(String content) {
		String key = "123456789";
		
		try {
			return CipherUtil.encryptAES(key,
					content);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "";
		}
	}
	
	public static String decryptAES(String content) {
		String key = "123456789";
		
		if (null == content)
			return null;
		
		try {
			return CipherUtil.decryptAES(key,
					content);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return "";
		}
	}
	
	/**
	 * 根据权重平摊一个数额，且每个分摊结果项定义了最大允许值
	 *
	 * @param total
	 * @param weights
	 * @return
	 */
	public static BigDecimal[] apportionByWeightsAndMaxLimit(BigDecimal total,
			BigDecimal[] weights, BigDecimal[] maxs, String currency) {

		assert total.compareTo(BigDecimal.ZERO) >= 0 && weights != null
				&& weights.length > 0 && maxs != null
				&& maxs.length == weights.length;

		//首先完全按照权重分
		BigDecimal[] resultsByWeights = apportionByWeights3(total, weights, currency);

		//若需要二次分配时的权重和最大值配置
		List<BigDecimal> reapportionWights = new ArrayList<BigDecimal>();
		List<BigDecimal> reapportionMaxs = new ArrayList<BigDecimal>();
		BigDecimal totalReapportionWeights = BigDecimal.ZERO;

		//分配值已确定为最大值的项的Index
		Set<Integer> fixedIndexes = new HashSet<Integer>();

		BigDecimal fixedTotal = BigDecimal.ZERO;
		for (int i = 0; i < resultsByWeights.length; i++) {
			if (resultsByWeights[i].compareTo(maxs[i]) > 0) {
				resultsByWeights[i] = maxs[i];
				fixedTotal = fixedTotal.add(maxs[i]);
				fixedIndexes.add(i);
			} else {
				reapportionWights.add(weights[i]);
				reapportionMaxs.add(maxs[i]);
				totalReapportionWeights = totalReapportionWeights
						.add(weights[i]);
			}
		}
		if (fixedIndexes.size() > 0) {
			if (reapportionMaxs.size() > 0
					&& totalReapportionWeights.compareTo(BigDecimal.ZERO) > 0) {
				BigDecimal[] reResult = apportionByWeightsAndMaxLimit(
						total.subtract(fixedTotal),
						reapportionWights.toArray(new BigDecimal[0]),
						reapportionMaxs.toArray(new BigDecimal[0]), currency);
				int j = 0;
				for (int i = 0; i < resultsByWeights.length; i++) {
					if (!fixedIndexes.contains(i)) {
						resultsByWeights[i] = reResult[j++];
					}
				}
			} else {
				throw new IllegalArgumentException("待分摊金额过大，无法按分摊比例及最大值限制分摊");
			}
		}

		return resultsByWeights;
	}

	public static int daysBetween(Date beginDate, Date endDate) {
		return Days.daysBetween(new DateTime(toDay(beginDate)),
				new DateTime(toDay(endDate))).getDays();
	}

	public static Date toDay(Date date) {
		Date result = DateUtils.setHours(date, 0);
		result = DateUtils.setMinutes(result, 0);
		result = DateUtils.setSeconds(result, 0);
		result = DateUtils.setMilliseconds(result, 0);
		return result;
	}

	/**
	 * 根据权重平摊一个数额  
	 * 分摊规则 全部舍
	 * 
	 * @param total
	 * @param weights
	 * @return
	 */
	public static BigDecimal[] apportionByWeights(BigDecimal total,
			BigDecimal[] weights) {

		assert total.compareTo(BigDecimal.ZERO) >= 0 && weights != null
				&& weights.length > 0;

		if (total.compareTo(BigDecimal.ZERO) == 0) {
			BigDecimal[] result = new BigDecimal[weights.length];
			Arrays.fill(result, BigDecimal.ZERO);
			return result;
		}

		BigDecimal totalWeight = BigDecimal.ZERO;

		for (BigDecimal weight : weights) {
			totalWeight = totalWeight.add(weight);
		}

		if (totalWeight.compareTo(BigDecimal.ZERO) == 0) {
			throw new IllegalArgumentException("分摊权重未设置");
		}

		BigDecimal[] result = new BigDecimal[weights.length];

		BigDecimal sum = BigDecimal.ZERO;

		int lastNotZeroIndex = -1;
		for (int i = 0; i < weights.length - 1; i++) {

			// MathContext.DECIMAL32 参数用于防止无限循环小数导致的异常
			result[i] = weights[i].divide(totalWeight, MathContext.DECIMAL32)
					.multiply(total)
					// 小数点后2位的直接舍去
					.setScale(2, RoundingMode.DOWN);
			sum = sum.add(result[i]);

			if (weights[i].compareTo(BigDecimal.ZERO) > 0) {
				lastNotZeroIndex = i;
			}

		}

		if (weights[weights.length - 1].compareTo(BigDecimal.ZERO) == 0) {
			result[lastNotZeroIndex] = result[lastNotZeroIndex].add(total
					.subtract(sum));
			result[weights.length - 1] = BigDecimal.ZERO;
		} else {
			result[weights.length - 1] = total.subtract(sum);
		}

		return result;

	}

	/**
	 * 根据权重平摊一个数额
	 * 分摊规则 4舍5入
	 * 
	 * @param total
	 * @param weights
	 * @return
	 */
	public static BigDecimal[] apportionByWeights2(BigDecimal total,
			BigDecimal[] weights) {

		assert total.compareTo(BigDecimal.ZERO) >= 0 && weights != null
				&& weights.length > 0;

		if (total.compareTo(BigDecimal.ZERO) == 0) {
			BigDecimal[] result = new BigDecimal[weights.length];
			Arrays.fill(result, BigDecimal.ZERO);
			return result;
		}

		BigDecimal totalWeight = BigDecimal.ZERO;

		for (BigDecimal weight : weights) {
			totalWeight = totalWeight.add(weight);
		}

		BigDecimal[] result = new BigDecimal[weights.length];

		BigDecimal sum = BigDecimal.ZERO;

		int lastNotZeroIndex = -1;
		for (int i = 0; i < weights.length - 1; i++) {

			// MathContext.DECIMAL32 参数用于防止无限循环小数导致的异常
			result[i] = weights[i].divide(totalWeight, MathContext.DECIMAL32)
					.multiply(total)
					// 小数点后2位的直接舍去
					.setScale(2, BigDecimal.ROUND_HALF_DOWN);
			sum = sum.add(result[i]);
			if (weights[i].compareTo(BigDecimal.ZERO) > 0) {
				lastNotZeroIndex = i;
			}
		}
		if (weights[weights.length - 1].compareTo(BigDecimal.ZERO) == 0) {
			result[lastNotZeroIndex] = result[lastNotZeroIndex].add(total
					.subtract(sum));
			result[weights.length - 1] = BigDecimal.ZERO;
		} else {
			result[weights.length - 1] = total.subtract(sum);
		}
		return result;
	}

	/**
	 * 根据权重平摊一个数额
	 * 分摊规则 4舍5入
	 * 
	 * @param total
	 * @param weights
	 * @return
	 */
	public static BigDecimal[] apportionByWeights3(BigDecimal total,
			BigDecimal[] weights, String currency) {
		
		if (null == currency)
			currency = "CNY";
		
		int scale = currency.equalsIgnoreCase("JPY") ? 0 : 2;
			
		assert total.compareTo(BigDecimal.ZERO) >= 0 && weights != null
				&& weights.length > 0;

		if (total.compareTo(BigDecimal.ZERO) == 0) {
			BigDecimal[] result = new BigDecimal[weights.length];
			Arrays.fill(result, BigDecimal.ZERO);
			return result;
		}

		BigDecimal totalWeight = BigDecimal.ZERO;

		for (BigDecimal weight : weights) {
			totalWeight = totalWeight.add(weight);
		}

		BigDecimal[] result = new BigDecimal[weights.length];

		BigDecimal sum = BigDecimal.ZERO;

		int lastNotZeroIndex = -1;
		for (int i = 0; i < weights.length - 1; i++) {

			// MathContext.DECIMAL32 参数用于防止无限循环小数导致的异常
			result[i] = weights[i]
					.divide(totalWeight, 6, RoundingMode.HALF_DOWN)
					.multiply(total)
					// 小数点后2位的直接舍去
					.setScale(scale, RoundingMode.HALF_DOWN);
			sum = sum.add(result[i]);
			if (weights[i].compareTo(BigDecimal.ZERO) > 0) {
				lastNotZeroIndex = i;
			}
		}
		if (weights[weights.length - 1].compareTo(BigDecimal.ZERO) == 0) {
			result[lastNotZeroIndex] = result[lastNotZeroIndex].add(total
					.subtract(sum));
			result[weights.length - 1] = BigDecimal.ZERO;
		} else {
			result[weights.length - 1] = total.subtract(sum);
		}
		return result;
	}

	public static String trimAndLeft(String src, int n) {
		return StringUtils.left(StringUtils.trim(src), n);
	}

	public static List<String> splitAndTrim(String src, String separator) {
		if (src == null) {
			return null;
		}
		String[] splits = src.split(separator);
		List<String> result = new ArrayList<String>();
		for (String split : splits) {
			String trim = split.trim();
			if (trim.length() > 0) {
				result.add(trim);
			}
		}
		return result;
	}

	/**
	 * 计算目标时间和当前时间的差的小时值
	 *
	 * @param dt
	 * @return
	 */
	public static int calcTimeSpanHour(Date dt) {
		return Hours.hoursBetween(new DateTime(dt), DateTime.now()).getHours();
	}

	/**
	 * 计算目标时间和当前时间的差的分钟值
	 *
	 * @param dt
	 * @return
	 */
	public static int calcTimeSpanMin(Date dt) {

		return Minutes.minutesBetween(new DateTime(dt), DateTime.now())
				.getMinutes();
	}
	
	/**
	 * 计算两个时间的差的秒钟值
	 *
	 * @param dt
	 * @return
	 */
	public static int calcTimeSpanSec(Date dt1, Date dt2) {

		return Seconds.secondsBetween(new DateTime(dt1), new DateTime(dt2))
				.getSeconds();
	}

	/**
	 * 将map 转换为 bean
	 * @param map
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static Object mapToObject(Map<String, Object> map, Class<?> beanClass)
			throws Exception {

		if (map == null)
			return null;

		Object obj = beanClass.newInstance();

		// 注册一个转换类型 
		org.apache.commons.beanutils.ConvertUtils.register(
				new SqlTimestampConverter(), java.util.Date.class);

		org.apache.commons.beanutils.BeanUtils.populate(obj, map);

		return obj;

	}

	/**
	 * 更新对象属性值
	 * @param ori 原始对象
	 * @param update 需要增量更新的对象
	 * @return
	 */
	public static Object updateObject(Object ori, Object update) {

		if (ori == null || update == null) {

			Logger.info("有空对象");

			return null;
		}

		if (!ori.getClass().equals(update.getClass())) {
			Logger.info("不是同一个对象");

			return null;
		}

		Field[] oriFields = ori.getClass().getDeclaredFields();
		Field[] updateFields = update.getClass().getDeclaredFields();

		for (int i = 0; i < updateFields.length; i++) {

			Field oriField = oriFields[i];
			Field updateField = updateFields[i];

			oriField.setAccessible(true);
			updateField.setAccessible(true);

			try {

				Object field = updateField.get(update);

				if (field == null || "".equals(field)) {
					continue;
				}

				oriField.set(ori, field);

			} catch (Exception e) {
				e.printStackTrace();
			}

		}

		return ori;

	}

	/**
	 * 返回默认币种
	 * @param currency
	 * @return
	 */
	public static String nullIsCNY(String currency) {
		if (null == currency)
			return "CNY";
		else
			return currency;
	}
	
	public static void main(String... args) {

		/*Logger.info(" %%:" + Utils.calcTimeSpanMin(null));

		Order o = new Order();
		Order o1 = new Order();
		o1.setBizId("22222222222222");

		o1.setProducts(new ArrayList<Product>());

		Refund p = new Refund();

		Utils.updateObject(o, o1);

		System.out.println(o);*/
		
		BigDecimal[] weights = new BigDecimal[2];
		
//		weights[3] = new BigDecimal("5660.00");
//		weights[2] = new BigDecimal("903.00");
		weights[1] = new BigDecimal("5800.00");
		weights[0] = new BigDecimal("803.00");
		
//		BigDecimal[] result = Utils.apportionByWeightsAndMaxLimit(
//				BigDecimal.TEN, weights,
//				maxLimits);
		
		BigDecimal[] result = Utils.apportionByWeights2(new BigDecimal("5645.56"), weights);
		
		for (int i = 0; i < result.length; i++) {
			System.out.println(result[i]);
		}
		
		
//		BigDecimal thirdPaid[] = Utils.apportionByWeights3(
//				new BigDecimal(1046), weights);

	}
}
